package roy.abc.study.WebServices.JavaWSUpAndRunning.ch01.soap;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Iterator;

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.Name;
import javax.xml.soap.Node;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DemoSoap {
	private static Logger logger = LoggerFactory.getLogger(DemoSoap.class);
	
	private static final String LocalName = "TimeRequest";
	private static final String NameSpace = "http://ch01/mysoap";
	private static final String NameSpacePrifix = "ms";
	
	private ByteArrayOutputStream out;
	private ByteArrayInputStream in;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new DemoSoap().request();
	}
	
	private void request(){
		SOAPMessage msg = createSoapMessage();
		
		try {
			// Inject the appropriate information into the message.
			// In this case, only the (optional) message header is used
			// and the body is empty.
			SOAPEnvelope evn = msg.getSOAPPart().getEnvelope();
			SOAPHeader hdr = evn.getHeader();
			
			// Add an element to the SOAP header.
			Name name = createQname(msg);
			hdr.addHeaderElement(name).addTextNode("time_request");
			
			// Simulate sending the SOAP message to a remote system by 
			// writing it to a ByteArrayOutputStream.
			out = new ByteArrayOutputStream();
			msg.writeTo(out);
			
			trace("The send SOAP message", msg);
			
			// Process response.
			SOAPMessage response = processRequest();
			extractContentsAndPrint(response);
			
		} catch (SOAPException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		} catch (IOException e){
			logger.error(e.getMessage());
			e.printStackTrace();
		}
	}
	
	private SOAPMessage processRequest(){
		processIncomingSoap();
		coodinateStreams();
		return createSoapMessage(in);
	}
	
	private void processIncomingSoap(){
		try {
			coodinateStreams();
			
			SOAPMessage msg = createSoapMessage(in);
			
			Name name = createQname(msg);
			
			SOAPHeader hdr = msg.getSOAPHeader();
			Iterator it = hdr.getChildElements(name);
			Node node = (Node) it.next();
			String value = (node == null) ? "Error!" : node.getValue();
			
			if (value.toLowerCase().contains("time_request")) {
				String now = new Date().toString();
				SOAPBody body = msg.getSOAPBody();
				body.addBodyElement(name).addTextNode(now);
				msg.saveChanges();
				
				msg.writeTo(out);
				trace("The recieved SOAP message", msg);
			}
			
		} catch (SOAPException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		} catch (IOException e){
			logger.error(e.getMessage());
			e.printStackTrace();
		}
	}
	
	private Name createQname(SOAPMessage msg){
		Name name = null;
		
		try{
			SOAPEnvelope evn = msg.getSOAPPart().getEnvelope();
			name = evn.createName(LocalName, NameSpacePrifix, NameSpace);
		} catch (SOAPException e){
			logger.error(e.getMessage());
			e.printStackTrace();
		}
		
		return name;
	}
	
	private void extractContentsAndPrint(SOAPMessage msg){
		try {
			SOAPBody body = msg.getSOAPBody();
			
			Name name = createQname(msg);
			Iterator it = body.getChildElements(name);
			Node node = (Node) it.next();
			
			String value = (node == null) ? "Error!" : node.getValue();
			logger.debug("Returned from server: " + value);
		} catch (SOAPException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		}
	}
	
	private SOAPMessage createSoapMessage(){
		SOAPMessage msg = null;
		
		try {
			MessageFactory factory = MessageFactory.newInstance();
			msg = factory.createMessage();
		} catch (SOAPException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		}
		
		return msg;
	}
	
	private SOAPMessage createSoapMessage(InputStream in){
		SOAPMessage msg = null;
		
		MessageFactory factory;
		try {
			factory = MessageFactory.newInstance();
			msg = factory.createMessage(null, in);
		} catch (SOAPException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			logger.error(e.getMessage());
			e.printStackTrace();			
		}
		
		return msg;
	}

	private void trace(String s, SOAPMessage msg){
		logger.debug(s);
		
		try {
			msg.writeTo(System.out);
			logger.debug("\n");
		} catch (SOAPException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		}
	}
	
	private void coodinateStreams(){
		in = new ByteArrayInputStream(out.toByteArray());
		out.reset();
	}
}
